/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.ui.editor.field;

import java.util.Collection;
import java.util.Map;

import com.daikit.commons.shared.bean.AbstractDkBeanWithId;
import com.daikit.commons.shared.utils.DkStringUtils;
import com.daikit.daikit4gxt.client.DkBaseMain;
import com.daikit.daikit4gxt.client.DkCacheEntity;
import com.daikit.daikit4gxt.client.labelprovider.DkAsSimpleStringLabelProvider;
import com.daikit.daikit4gxt.client.ui.edit.BeforeOpenEvent.BeforeOpenHandler;
import com.daikit.daikit4gxt.client.ui.edit.BeforeOpenEvent.HasBeforeOpenHandlers;
import com.daikit.daikit4gxt.client.ui.edit.ValueChangeToNullOrSelectionChangeEvent;
import com.daikit.daikit4gxt.client.ui.edit.ValueChangeToNullOrSelectionChangeEvent.HasValueChangeToNullOrSelectionChangeHandlers;
import com.daikit.daikit4gxt.client.ui.edit.ValueChangeToNullOrSelectionChangeEvent.ValueChangeToNullOrSelectionChangeHandler;
import com.daikit.daikit4gxt.client.ui.editor.field.DkComboboxCell.ItemLocaleQueryMatcher;
import com.daikit.daikit4gxt.client.ui.editor.field.DkComboboxCell.MyDynamicComboboxCssBundle;
import com.daikit.daikit4gxt.client.ui.editor.field.DkComboboxCell.MyDynamicComboboxXTemplate;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.cell.core.client.form.ComboBoxCell;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.widget.core.client.form.ComboBox;


/**
 * Dynamic {@link ComboBox}
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <B>
 */
public class DkComboBox<B> extends ComboBox<B> implements HasValueChangeToNullOrSelectionChangeHandlers<B>,
		HasBeforeOpenHandlers<B>
{

	/**
	 * Create a new {@link DkComboBox}
	 * 
	 * @param cacheEntity
	 *           the {@link DkCacheEntity} to get data and label provider from
	 */
	public DkComboBox(final DkCacheEntity<B> cacheEntity)
	{
		this(cacheEntity, cacheEntity.getLabelProvider());
	}

	/**
	 * Create a new {@link DkComboBox} with a {@link ModelKeyProvider} taking the id of bean if instanceof
	 * {@link AbstractDkBeanWithId} or the item.toString. The generated {@link LabelProvider} is printing
	 * item.getAsSimpleString() if instanceof {@link AbstractDkBeanWithId} or the item.toString
	 * 
	 * @param possibleValues
	 *           the {@link Collection} of possible values
	 */
	public DkComboBox(final Collection<B> possibleValues)
	{
		this(possibleValues, new DkAsSimpleStringLabelProvider<B>());
	}

	/**
	 * Create a new {@link DkComboBox} with a {@link ModelKeyProvider} taking the id of bean if instanceof
	 * {@link AbstractDkBeanWithId} or the item.toString.
	 * 
	 * @param possibleValues
	 *           the {@link Collection} of possible values
	 * @param itemToLabelMap
	 *           the {@link Map} containing item as keys and labels as values
	 */
	public DkComboBox(final Collection<B> possibleValues, final Map<B, String> itemToLabelMap)
	{
		this(possibleValues, new LabelProvider<B>()
		{
			@Override
			public String getLabel(final B item)
			{
				return itemToLabelMap.get(item);
			}
		});
	}

	/**
	 * Create a new {@link DkComboBox} with a {@link ModelKeyProvider} taking the id of bean if instanceof
	 * {@link AbstractDkBeanWithId} or the item.toString.
	 * 
	 * @param possibleValues
	 *           the {@link Collection} of possible values
	 * @param labelProvider
	 *           the {@link LabelProvider}
	 */
	public DkComboBox(final Collection<B> possibleValues, final LabelProvider<B> labelProvider)
	{
		this(new ListStore<B>(new ModelKeyProvider<B>()
		{
			@Override
			public String getKey(final B item)
			{
				return item instanceof AbstractDkBeanWithId ? ((AbstractDkBeanWithId) item).getId() : DkStringUtils.toString(item);
			}
		}), labelProvider);
		getStore().addAll(possibleValues);
	}

	/**
	 * Create a new {@link DkComboBox}
	 * 
	 * @param cacheEntity
	 *           the {@link DkCacheEntity} to get data and label provider from
	 * @param labelProvider
	 *           the {@link LabelProvider} it will be used for ComboBox content and choices
	 */
	public DkComboBox(final DkCacheEntity<B> cacheEntity, final LabelProvider<? super B> labelProvider)
	{
		this(cacheEntity.createListStore(), labelProvider);
	}

	/**
	 * Create a new {@link DkComboBox}
	 * 
	 * @param store
	 *           the {@link Store}
	 */
	public DkComboBox(final ListStore<B> store)
	{
		super(new DkComboboxCell<B>(store, new DkAsSimpleStringLabelProvider<B>()));
		addInternalHandlers();
		setWidth(DkBaseMain.config().getDynamicComboboxWidth());
	}

	/**
	 * Create a new {@link DkComboBox}
	 * 
	 * @param store
	 *           the {@link Store}
	 * @param labelProvider
	 *           the {@link LabelProvider} it will be used for ComboBox content and choices
	 */
	public DkComboBox(final ListStore<B> store, final LabelProvider<? super B> labelProvider)
	{
		super(new DkComboboxCell<B>(store, labelProvider));
		addInternalHandlers();
		setWidth(DkBaseMain.config().getDynamicComboboxWidth());
	}

	/**
	 * Create a new {@link DkComboBox}
	 * 
	 * @param store
	 *           the {@link Store}
	 * @param labelProvider
	 *           the {@link LabelProvider} it will be used for ComboBox content
	 * @param choicesTemplate
	 *           it will be used for choices and return HTML
	 * @param cssBundle
	 *           a clientBundle permitting to retrieve a CSS file to be used to render choicesTemplate
	 */
	public DkComboBox(final ListStore<B> store, final LabelProvider<B> labelProvider,
			final MyDynamicComboboxXTemplate<B> choicesTemplate, final MyDynamicComboboxCssBundle<?> cssBundle)
	{
		super(new DkComboboxCell<B>(store, labelProvider, choicesTemplate, cssBundle));
		addInternalHandlers();
		setWidth(DkBaseMain.config().getDynamicComboboxWidth());
	}

	/**
	 * Create a new {@link DkComboBox} from given cell
	 * 
	 * @param cell
	 *           the cell
	 */
	protected DkComboBox(final ComboBoxCell<B> cell)
	{
		super(cell);
		addInternalHandlers();
		setWidth(DkBaseMain.config().getDynamicComboboxWidth());
	}

	protected void addInternalHandlers()
	{
		addValueChangeHandler(new ValueChangeHandler<B>()
		{
			@Override
			public void onValueChange(final ValueChangeEvent<B> event)
			{
				if (event.getValue() == null)
				{
					fireEvent(new ValueChangeToNullOrSelectionChangeEvent<B>(null));
				}
			}
		});
		addSelectionHandler(new SelectionHandler<B>()
		{
			@Override
			public void onSelection(final SelectionEvent<B> event)
			{
				fireEvent(new ValueChangeToNullOrSelectionChangeEvent<B>(event.getSelectedItem()));
			}
		});
	}

	@Override
	public HandlerRegistration addValueChangeToNullOrSelectionChangeHandler(
			final ValueChangeToNullOrSelectionChangeHandler<B> handler)
	{
		return addHandler(handler, ValueChangeToNullOrSelectionChangeEvent.getType());
	}

	@Override
	public HandlerRegistration addBeforeOpenHandler(final BeforeOpenHandler<B> handler)
	{
		return ((DkComboboxCell<B>) getCell()).addBeforeOpenHandler(handler);
	}

	/**
	 * Set a matcher for local filtering
	 * 
	 * @param itemLocalQueryMatcher
	 *           a {@link ItemLocaleQueryMatcher}
	 */
	public void setItemLocalQueryMatcher(final ItemLocaleQueryMatcher<B> itemLocalQueryMatcher)
	{
		((DkComboboxCell<B>) getCell()).setItemLocalQueryMatcher(itemLocalQueryMatcher);
	}

}
